import 'reflect-metadata'

/**
 * 工厂 返回一个函数 \
 * 返回的函数也是一个工厂 调用后返回参数装饰器函数
 * @param key 是参数装饰器的名称 Request 或者是一个自定义装饰器的函数
 */
export const createParamDecorator = (keyOrFactory: string | Function) => {
  return (data?: any) => {
    // 参数装饰器在类的原型方法时使用时 接收 3 个参数
    // 类的原型 参数名 参数索引( 从左往右 从0开始 )
    // 参数装饰器执行时 是从右往左依次执行
    return (target: any, propertyKey: string, parameterIndex: number) => {
      console.log(target, propertyKey, parameterIndex)
      // 如果第一次从类原型的方法名上取不到 params 元数据则为 []
      // 如果第二次及后续则可以从类原型的方法名上取到 params 元数据的值
      const existingParameters = Reflect.getMetadata('params', target, propertyKey) || []
      if (keyOrFactory instanceof Function) { // 自定义参数装饰器
        // key 设置为 DecoratorFactory 表示是自定义参数装饰器
        // factory 是自定义参数装饰器的工厂函数
        existingParameters[parameterIndex] = { parameterIndex, key: 'DecoratorFactory', factory: keyOrFactory, data }
      } else {
        // parameterIndex是参数的索引 
        // key是参数装饰器名 比如 'Request' 或 'Req' 或 'Query 等
        // data 是参数装饰器的参数 可能有也可能没有
        // 有可能是一个稀疏数组 但 map 不会在 稀疏数组 的空槽处调用
        existingParameters[parameterIndex] = { parameterIndex, key:keyOrFactory, data }
      }

      // 类装饰器原型对象上方法名添加一个 `params:方法名` 元数据 值是一个 []
      Reflect.defineMetadata(`params`, existingParameters, target, propertyKey)
    }
  }
}

export const Request = createParamDecorator('Request') // 获取请求对象
export const Req = createParamDecorator('Req') // 是 Request 参数装饰器的别名

export const Query = createParamDecorator('Query') // 获取查询参数
export const Headers = createParamDecorator('Headers') // 获取请求头

export const Session = createParamDecorator('Session') // 获取会话功能 ( 需要安装 express-session 中间件和 @types/express-session 类型 )
export const Ip = createParamDecorator('Ip') // 获取请求IP

export const Param = createParamDecorator('Param') // 获取请求路径参数

export const Body = createParamDecorator('Body') // 获取请求体 ( 需要配置 express.json() 中间件 和 express.urlencoded() 中间件 )

// 默认情况下如果使用了 @Res() 或 @Response() 则需要手动管理如何响应 如果不手动响应则会被挂起
// 添加 {passthrough: true} 则会将响应控制权重新交给 nestjs 来处理
export const Response = createParamDecorator('Response') // 获取响应对象
export const Res = createParamDecorator('Res') // 是 Response 参数装饰器的别名

export const Next = createParamDecorator('Next') // 获取下一个中间件逻辑的函数

